#ifndef UUID_D780899DBDCE4B612AFA9A8C94298169
#define UUID_D780899DBDCE4B612AFA9A8C94298169

#include <gvl/containers/range.hpp>
#include <gvl/io/common_bitstream.hpp>
#include <gvl/support/log.hpp>

namespace Psync5
{

struct PacketModel
{
	PacketModel(uint32_t channelCountInit)
	: channelCount(channelCountInit)
	, expectedSectionId(0)
	, sectionCount(0)
	, header(0)
	{
	}

	void putSection(Packet& destPacket, Section& section)
	{
		/* New proposed short format:

		llllmmm0 -> next section id, [1, 16] bytes, [1, 8] message ends, partial == false

		We don't expect partial sections this short so it's useless to use bits for encoding them.
		Due to that, we always expect at least 1 message end since 0 implies partial == true.

		Problem with message ends and range coding:

		The problem is counting the message ends to the right section. The problem
		can be solved by delaying message end incrementing for one byte (or more,
		depends on the normalization behaviour of the coder) after
		we encountered the message end. This makes sure all data is available
		to reach the message end. It could theoretically delay a message unnecessarily
		but flushing should have been done if delay wasn't acceptable anyway.

		The practical implemention could be to have a 2 element queue for delaying:
			if(messageEnd)
			   ++q1;

		Writing byte:
			section->messageEnds += q0;
			q0 = q1
			q1 = 0

		This is kind of a problem with a carry range coder unless we reserve bytes and properly
		move to a new section when reserved bytes fill up the previous one.

		We can do better than this in theory, by calculating how many bytes are required for
		storing the current number and delaying by that amount, but it requires more
		computation.

		== New proposed long format:

		lllllll1 mmmmmmll mmmmmmmm ssssssss ssssssss

		(ssssssss | ssssssss mmmmmmmm mmmmmmll lllllll1)

		[1, 511] bytes + partial flag
		[0, 16383] messages
		[0, 65535] section id
		*/

		// TODO: Larger section numbers

		sassert(section.sizeBytes > 0 && section.sizeBytes <= Section::MaxBufByteSize);
		sassert(!section.partial || section.sizeBytes == Section::MaxBufByteSize);
		sassert(section.partial || section.messageEnds > 0);
		sassert(section.messageEnds <= 16383);
		sassert(section.id <= 65535);

		if(section.sizeBytes <= 16
		&& section.id == expectedSectionId
		&& section.messageEnds <= 8)
		{
			destPacket.write8(
				((section.sizeBytes - 1) << 4) |
				((section.messageEnds - 1) << 1));
		}
		else
		{
			destPacket.write32(
				1 |
				((section.sizeBytes - 1 + section.partial) << 1) |
				(section.messageEnds << 10) |
				((section.id & 0xff) << 24));

			destPacket.write8(section.id >> 8);
		}

		destPacket.write(section.buffer, section.sizeBytes);

		TLOG(this << ": written section, size " << section.sizeBytes << ", message ends " << section.messageEnds << ", partial: " << (int)section.partial);
	}

	void getSection(Packet& srcPacket, Section& section)
	{
		uint32_t b1 = srcPacket.read8();
		if((b1 & 1) == 0)
		{
			section.sizeBytes = ((b1 >> 4) + 1);
			section.messageEnds = ((b1 >> 1) & 7) + 1;
			section.partial = false;
			section.id = expectedSectionId;
		}
		else
		{
			uint32_t b2 = srcPacket.read32();

			uint32_t sizeBytes = (((b1 >> 1) | (b2 << 7)) & 0x1ff) + 1;
			section.messageEnds = (b2 >> 2) & ((1<<14)-1);
			if(sizeBytes == Section::MaxBufByteSize + 1)
			{
				section.partial = true;
				--sizeBytes;
			}
			else
			{
				section.partial = false;
			}
			section.sizeBytes = sizeBytes;
			section.id = (b2 >> 16);
		}

		srcPacket.read(section.buffer, section.sizeBytes);

		TLOG(this << ": read section, size " << section.sizeBytes << ", message ends " << section.messageEnds << ", partial: " << (int)section.partial);
	}

	void finishSection(Section& section)
	{
		expectedSectionId = section.id + 1;
		++sectionCount;
	}

	void finish()
	{
		*header = sectionCount;
	}

	void reserveHeader(uint32_t* p)
	{
		header = p;
	}

	void readHeader(uint32_t h)
	{
		sectionCount = h;
	}

	uint32_t expectedSectionId;
	uint32_t channelCount;
	uint32_t sectionCount;
	uint32_t* header;
};

}

#endif // UUID_D780899DBDCE4B612AFA9A8C94298169
